// this code is from MSDN : http://msdn.microsoft.com/en-us/library/windows/desktop/ms737591(v=vs.85).aspx
// I believe the homework did not required to implement client.
// And I have adjusted some source to send a packet as homework required.

#include "stdafx.h"

#pragma comment( lib, "Ws2_32.lib"   )
#pragma comment( lib, "Mswsock.lib"  )
#pragma comment( lib, "AdvApi32.lib" )

//===========================================================================
#define DEFAULT_BUFLEN 512
#define DEFAULT_PORT "1234"

//===========================================================================
const unsigned int BUFFER_SIZE = 1024;

//===========================================================================
struct Builder
{
    Builder( void )
        : pos( 0 )
    {}
    
    char     buffer[ BUFFER_SIZE ];
    unsigned pos;

    virtual bool Build( void ) = 0;
    
    char * Ptr( void ) {
        return this->buffer;
    }

    unsigned Size( void ) {
        return this->pos;
    }

    void Cleanup( void ) {
        this->pos = 0;
    }
    
    void Append(
        const void *   data,
              unsigned size
    ) {
        ::memcpy(
            this->buffer + this->pos,
            data,
            size
        );
        this->pos += size;
    }
    
    void operator << ( const char & data ) {
        this->Append(
            &data,
            sizeof( char )
        );
    }
    
    void operator << ( const short & data ) {
        this->Append(
            &data,
            sizeof( short )
        );
    }

    void operator << ( const int & data ) {
        this->Append(
            &data,
            sizeof( int )
        );
    }

    void operator << ( const char * data ) {
        unsigned int len = ::strlen( data );
        Append( data, len );

        static const char null_terminator = '\0';
        Append( &null_terminator, sizeof( char ) );
    }
};

//===========================================================================
struct Header
{
    char  version;
    short type;
    int   user;

    Header( void )
        : version( 0 )
        , type(    0 )
        , user(    0 )
    {}
};

//===========================================================================
struct Packet
    : public Header
    , public Builder
{
    char * payload;

    Packet( void ) {}

    bool Build( void ) {
        this->Cleanup();

        this->type = htons( this->type );
        this->user = htonl( this->user );
        
        *this << this->version;
        *this << this->type;
        *this << this->user;
        *this << this->payload;
        
        return true;
    }
};

//===========================================================================
int _tmain(
    int      argc,
    _TCHAR * argv[]
) {
    WSADATA wsaData;
    SOCKET ConnectSocket = INVALID_SOCKET;
    struct addrinfo * result = NULL;
    struct addrinfo * ptr    = NULL;
    struct addrinfo   hints;
    char *sendbuf = "this is a test";
    //char recvbuf[DEFAULT_BUFLEN];
    int iResult;
    int recvbuflen = DEFAULT_BUFLEN;
    
    // Initialize Winsock
    iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
    if (iResult != 0) {
        printf("WSAStartup failed with error: %d\n", iResult);
        return 1;
    }

    ZeroMemory( &hints, sizeof(hints) );
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;

    // Resolve the server address and port
    iResult = getaddrinfo( "127.0.0.1", DEFAULT_PORT, &hints, &result);
    if ( iResult != 0 ) {
        printf("getaddrinfo failed with error: %d\n", iResult);
        WSACleanup();
        return 1;
    }

    // Attempt to connect to an address until one succeeds
    for(ptr=result; ptr != NULL ;ptr=ptr->ai_next) {

        // Create a SOCKET for connecting to server
        ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, 
            ptr->ai_protocol);
        if (ConnectSocket == INVALID_SOCKET) {
            printf("socket failed with error: %ld\n", WSAGetLastError());
            WSACleanup();
            return 1;
        }

        // Connect to server.
        iResult = connect( ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
        if (iResult == SOCKET_ERROR) {
            closesocket(ConnectSocket);
            ConnectSocket = INVALID_SOCKET;
            continue;
        }
        break;
    }

    freeaddrinfo(result);

    if (ConnectSocket == INVALID_SOCKET) {
        printf("Unable to connect to server!\n");
        WSACleanup();
        return 1;
    }

    Packet packet;
    packet.version = 1;
    packet.type    = 2;
    packet.user    = 3;
    packet.payload = "PAYLOAD";
    packet.Build();
    
    char *   buf  = packet.Ptr();
    unsigned size = packet.Size();
    char buffer[ 4096 ] = { 0, };
    for( unsigned int i = 0; i < 3; i++ )
        memcpy( buffer + ( i * size ), buf, size );

    send( ConnectSocket, buffer,      10, 0 );
    ::Sleep( 1000 * 2 );
    send( ConnectSocket, buffer + 10, 15, 0 );
    ::Sleep( 1000 * 2 );
    send( ConnectSocket, buffer + 25, 15, 0 );
    ::Sleep( 1000 * 2 );
    send( ConnectSocket, buffer + 40,  5, 0 );
    
    /*
    // Send an initial buffer
    iResult = send( ConnectSocket, packet.Ptr(), packet.Size(), 0 );
    if (iResult == SOCKET_ERROR) {
        printf("send failed with error: %d\n", WSAGetLastError());
        closesocket(ConnectSocket);
        WSACleanup();
        return 1;
    }
    */

    printf("Bytes Sent: %ld\n", iResult);

    // shutdown the connection since no more data will be sent
    iResult = shutdown(ConnectSocket, SD_SEND);
    if (iResult == SOCKET_ERROR) {
        printf("shutdown failed with error: %d\n", WSAGetLastError());
        closesocket(ConnectSocket);
        WSACleanup();
        return 1;
    }

    /*
    // Receive until the peer closes the connection
    do {

        iResult = recv(ConnectSocket, recvbuf, recvbuflen, 0);
        if ( iResult > 0 )
            printf("Bytes received: %d\n", iResult);
        else if ( iResult == 0 )
            printf("Connection closed\n");
        else
            printf("recv failed with error: %d\n", WSAGetLastError());

    } while( iResult > 0 );
    */

    getchar();

    // cleanup
    closesocket(ConnectSocket);
    WSACleanup();

    return 0;
}

